Skip to content

Comments

feat: add wasm-dot package for Polkadot transaction building and decoding#145

Open
lcovar wants to merge 2 commits intomasterfrom
BTC-0.dot-wasm
Open

feat: add wasm-dot package for Polkadot transaction building and decoding#145
lcovar wants to merge 2 commits intomasterfrom
BTC-0.dot-wasm

Conversation

@lcovar
Copy link
Contributor

@lcovar lcovar commented Feb 6, 2026

Summary

add wasm-dot package for Polkadot/Substrate transaction building, parsing, and signing. follows wasm-utxo conventions: Uint8Array only, signing separate from parsing, get wasm() for internal access.

API

three entry points:

  1. DotTransaction.fromBytes(bytes) — deserialize for signing
  2. parseTransaction(bytes, context) — decode into structured data
  3. explainTransaction(bytes, options) — high-level explanation with type derivation
// signing
const tx = DotTransaction.fromBytes(txBytes);
tx.addSignature(signature, pubkey);
const signedBytes = tx.toBytes();

// parsing
const parsed = parseTransaction(txBytes, { material });
console.log(parsed.method.pallet); // "balances"
console.log(parsed.method.name);   // "transferKeepAlive"

// explaining
const explained = explainTransaction(txBytes, { context: { material } });
console.log(explained.type); // TransactionType.Send
console.log(explained.outputs); // [{ address, amount }]

architecture

Rust (parser.rs, builder/) → SCALE encoding/decoding
TS (explain.ts)            → business logic (type derivation, output extraction)

rust handles SCALE binary format (compact integers, MultiAddress, era encoding, recursive batch/proxy calls). typescript handles explain layer — deriving transaction types from pallet+method, extracting outputs/inputs. business logic changes don't require WASM rebuilds.

what's included

builder — build from declarative intents:

  • transfer, transferAll, stake, unstake, withdrawUnbonded, chill
  • addProxy, removeProxy
  • batch / batchAll with nested call encoding
  • accepts JS BigInt for amounts (u64 — matches wasm-solana)
  • mortal/immortal eras, tips, nonce

parser — decode SCALE extrinsics:

  • extracts sender, nonce, tip, era, method (pallet + name + decoded args)
  • hardcoded call resolution for Polkadot, Kusama, Westend
  • dynamic metadata-based resolution for any Substrate chain
  • recursive parsing for utility.batch and proxy.proxy (depth-limited, max 10)

explain — structured transaction explanation:

  • derives TransactionType: Send, StakingActivate, StakingUnlock, StakingWithdraw, StakingUnvote, StakingClaim, AddressInitialization, Batch, Unknown
  • recursive output/input extraction through batch and proxy wrappers
  • computes outputAmount (sum of non-ALL outputs)

conventions followed

  • Uint8Array everywhere (no hex methods on Transaction)
  • parsing separate from Transaction class (standalone parseTransaction function)
  • get wasm() accessor (not getInner())
  • DotTransaction.fromBytes() for signing (no top-level parseTransaction that returns Transaction)
  • callers do their own base conversions

test coverage

  • 28 Rust unit tests — parser, address encoding, call encoding, type serialization
  • 30 TypeScript integration tests — build+explain round-trips for all transaction types, batch output extraction, unsigned transactions, metadata fields

test plan

  • cargo test — 28 passing
  • cargo fmt + cargo clippy — clean
  • npm run build — WASM + TypeScript compilation
  • npm test — 30 passing
  • npx prettier --check — clean

Tickets

  • BTC-3064 — Transaction deserialization and signing
  • BTC-3065 — Transaction parsing and call data decoding
  • BTC-3066 — Transaction explanation
  • BTC-3067 — Transaction building

@lcovar lcovar force-pushed the BTC-0.dot-wasm branch 2 times, most recently from 58a71f1 to 751d3e2 Compare February 19, 2026 22:33
@lcovar lcovar changed the title temp: add wasm-dot package for Polkadot feat: add wasm-dot package for Polkadot transaction building and decoding Feb 19, 2026
@lcovar lcovar force-pushed the BTC-0.dot-wasm branch 11 times, most recently from 3be05b1 to fe73139 Compare February 20, 2026 09:17
…ding

WASM-based Polkadot/Substrate transaction builder, parser, and explainer
for use by sdk-coin-dot (tdot). Supports transfers, staking operations,
proxy management, and batched calls.

Key capabilities:
- Build unsigned extrinsics from high-level intents (transfer, stake, batch, proxy)
- Parse signed/unsigned extrinsics with metadata-aware signed extension decoding
- Explain transactions: derive type, extract outputs/inputs, attach DOT-specific metadata
- Proxy type resolution from chain metadata (works across Polkadot, Kusama, Westend)
- parseTransaction returns DotTransaction with .parse() method (PSBT pattern)

BTC-0
@lcovar lcovar marked this pull request as ready for review February 20, 2026 20:21
@lcovar lcovar requested a review from a team as a code owner February 20, 2026 20:21
Copy link
Contributor

@OttoAllmendinger OttoAllmendinger left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

let's try this: I will write a high level CONVENTIONS.md file that contains these re-occurring patterns an we'll see if the agents pick it up

lcovar added a commit that referenced this pull request Feb 23, 2026
remove hex methods, move parse() to standalone function, use get wasm()

changes per otto's review:
- remove Transaction.fromHex(), .toHex(), .toBroadcastFormat(), .callDataHex, .signablePayloadHex()
- remove _context field and .parse() method from Transaction
- rename parseTransactionData → parseTransaction (the actual parser)
- rename getInner() → get wasm() (@internal)
- remove top-level parseTransaction function (callers use DotTransaction.fromBytes directly)
- move createParseContext to parser.ts (single source)
- update tests: DotTransaction.fromBytes() for signing, parseTransaction() for decoded data
- callers do their own base conversions (Uint8Array only on public API)

separation of concerns:
- Transaction.fromBytes(bytes) - deserialize for signing
- parseTransaction(bytes, context?) - decode into structured data
- explainTransaction(bytes, options) - high-level explanation

BTC-0
remove hex methods, move parse() to standalone function, use get wasm()

changes per otto's review:
- remove Transaction.fromHex(), .toHex(), .toBroadcastFormat(), .callDataHex, .signablePayloadHex()
- remove _context field and .parse() method from Transaction
- rename parseTransactionData → parseTransaction (the actual parser)
- rename getInner() → get wasm() (@internal)
- remove top-level parseTransaction function (callers use DotTransaction.fromBytes directly)
- move createParseContext to parser.ts (single source)
- update tests: DotTransaction.fromBytes() for signing, parseTransaction() for decoded data
- callers do their own base conversions (Uint8Array only on public API)

separation of concerns:
- Transaction.fromBytes(bytes) - deserialize for signing
- parseTransaction(bytes, context?) - decode into structured data
- explainTransaction(bytes, options) - high-level explanation

BTC-0
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants